/** 
    @file game.cpp
    @brief The main game class
*/
#include "game.hpp"

template<> pm3::GAME* Ogre::Singleton<pm3::GAME>::ms_Singleton = 0;

pm3::GAME::GAME() {
    m_frame_listener = NULL;
    m_viewport = NULL;

    m_is_opengl = true;
}

pm3::GAME::~GAME() {
    cleanup();
}

void pm3::GAME::config() {
    LOGGER *log = LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    if (log)
        log->report( "Init", "I: Loading Ogre configuration, log and plugins\n" );

    // Configure Ogre
	intf->m_root = new Ogre::Root( "plugins.cfg", "ogre.cfg", "ogre.log" );
    // Open the configuration dialog, if run for the first time
	if (!intf->m_root->restoreConfig()) {
		if (!intf->m_root->showConfigDialog()) {
            if (log)
                log->report( "Init", "E: Failed in both loading configuration and showing configuration dialog\n" );
			return;
        }
    }

    if (!intf->get_root()) {
        if (log)
            log->report( "Init", "I: Global interface root node pointer uninitialized\n" );
        return;
    }

    if (log)
        log->report( "Init", "I: Initializing render window\n" );

    // Initialise a rendering window
	intf->m_render_window = intf->get_root()->initialise( true, "PixelMaz3" );

    if (log)
        log->report( "Init", "I: Loading resource configuration\n" );

    // Read the resources configuration file for initial resources
	Ogre::ConfigFile config_file;

	config_file.load( "resources.cfg" );

    // Loop through the entries
	Ogre::ConfigFile::SectionIterator sec_it = config_file.getSectionIterator();

	while (sec_it.hasMoreElements()) {
		Ogre::String sec_name = sec_it.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap* settings = sec_it.getNext();

		for (Ogre::ConfigFile::SettingsMultiMap::iterator i = settings->begin(); i != settings->end(); ++i) {
			Ogre::String type_name = i->first;
			Ogre::String arch_name = i->second;

            // Add all the resource locations
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation( arch_name, type_name, sec_name );
		}
	}

    if (log)
        log->report( "Init", "I: Loading resources\n" );

    // And load them
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void pm3::GAME::setup_listeners() {
    LOGGER *log = LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    // Create a frame listener
    if (intf->m_render_window) {
	    m_frame_listener = new pm3::FRAME_LISTENER();
        // Initialize it
        m_frame_listener->init();
    }
    // And attach it to the root node
    if (intf->m_root)
    	intf->m_root->addFrameListener( m_frame_listener );
}

void pm3::GAME::setup_shadows() {
    LOGGER *log = LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    if (!intf) {
        if (log)
            log->report( "Init", "E: Global interface uninitialized\n" );
        return;
    }

    // Are we using OpenGL?
    m_is_opengl = ( intf->get_root()->getRenderSystem()->getName().find("GL") != std::string::npos );

	// do this first so we generate edge lists
/*	if (intf->get_root()->getRenderSystem()->getCapabilities()->hasCapability( RSC_HWSTENCIL )) {
        intf->get_scenemgr()->setShadowTechnique( SHADOWTYPE_STENCIL_ADDITIVE );
        m_shadow_tech = SHADOWTYPE_STENCIL_ADDITIVE;
    } else {
        intf->get_scenemgr()->setShadowTechnique( SHADOWTYPE_TEXTURE_MODULATIVE );
        m_shadow_tech = SHADOWTYPE_TEXTURE_MODULATIVE;
    }*/

    //! \bug Shadows are broken with multiple lightsources

    intf->get_scenemgr()->setShadowTechnique( Ogre::SHADOWTYPE_TEXTURE_MODULATIVE );

	if (intf->get_root()->getRenderSystem()->getCapabilities()->hasCapability( Ogre::RSC_HWRENDER_TO_TEXTURE )) {
        // In D3D, use a 1024x1024 shadow texture
        intf->get_scenemgr()->setShadowTextureSettings( 1024, 2 );
    } else {
        // Use 512x512 texture in GL since we can't go higher than the window res
        intf->get_scenemgr()->setShadowTextureSettings( 512, 2 );
    }

//    intf->get_scenemgr()->setShadowTechnique( Ogre::SHADOWTYPE_STENCIL_MODULATIVE );

//    intf->get_scenemgr()->setShadowTextureSelfShadow( true );
    intf->get_scenemgr()->setShadowColour( Ogre::ColourValue( 0.5, 0.5, 0.5 ) );
    intf->get_scenemgr()->setShadowFarDistance( 10 );
}

void pm3::GAME::go() {
    LOGGER *log = LOGGER::getSingletonPtr();
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();
    pm3::PIXELWORLD *world = pm3::PIXELWORLD::getSingletonPtr();
    pm3::PHYSICS *physics = pm3::PHYSICS::getSingletonPtr();
    Ogre::Camera *camera = NULL;

    srand( time( NULL ) );

    if (log)
        log->report( "Pixel", "I: Loading configuration..\n" );

    // Load the configuration files
	config();

    if (log)
        log->report( "Pixel", "I: Setting up listeners..\n" );

    // Set up listeners
	setup_listeners();

    if (log)
        log->report( "Pixel", "I: Initializing world..\n" );

    // Create a portal connected zone scene manager
//	intf->m_scene_mgr = intf->get_root()->createSceneManager( Ogre::ST_GENERIC, "GenericSceneManager" );
    intf->m_scene_mgr = (Ogre::PCZSceneManager *) intf->get_root()->createSceneManager( "PCZSceneManager", "PCZSceneManager" );
    intf->m_scene_mgr->init( "ZoneType_Default" );
//    intf->m_scene_mgr->setShowPortals( true );

    // Enable shadowcasting
    //! \bug Tears FPS by about 15 on windows
    setup_shadows();
    // Initialize physics
    physics->init();

    // Initialize the world
    world->init();

    physics->get_physics_debug()->setNode( pm3::INTERFACE::getSingleton().get_world_node() );

    world->load( "../media/world/world.lua" );

    // And a viewport along with the camera
/*    camera = world->m_player.get_camera();
	m_viewport = intf->get_render_window()->addViewport( camera );
	camera->setAspectRatio( Ogre::Real( m_viewport->getActualWidth() / m_viewport->getActualHeight() ) );*/

    // Create the text overlay
    m_text_overlay = new pm3::hud::TEXT_OVERLAY();

    // Add a camera orientation and position textbox
    pm3::hud::TEXT_OVERLAY::getSingleton().add_textbox( "dbgCam", "<Camera debuginfo>", 10, 10, 100, 20, Ogre::ColourValue::Green );
    // Add an FPS textbox
    pm3::hud::TEXT_OVERLAY::getSingleton().add_textbox( "FPS", "<Frames per second>", 10, 20, 100, 40, Ogre::ColourValue::Green );

    if (log)
        log->report( "Pixel", "I: Starting rendering..\n" );

    // And start the rendering loop
	intf->m_root->startRendering();

    if (log)
        log->report( "Pixel", "I: Cleaning up..\n" );

    // Clean it all up
    cleanup();
}

void pm3::GAME::cleanup() {
    pm3::INTERFACE *intf = pm3::INTERFACE::getSingletonPtr();

    // Ogre overlays should be destroyed before Ogre Root
    if (m_text_overlay) {
        delete m_text_overlay;
        m_text_overlay = NULL;
    }

    if (!intf)
        return;

    // Destroy Ogre Root
    if (intf->m_root) {
        // But before that, the frame listener should be uninstalled
        if (m_frame_listener) {
            // Remove the frame listener from Ogre Root
            intf->m_root->removeFrameListener( m_frame_listener );
            // Delete it
            delete m_frame_listener;
            m_frame_listener = NULL;
        }
        // Delete the root node along with everything else
        delete intf->m_root;
        intf->m_root = NULL;
    }
}

